iT邦幫忙

2025 iThome 鐵人賽

DAY 22
0

前面一篇已經介紹了Android的SQL使用方式,且配置好了dependency,希望明確的步驟可以使這塊好懂一些

建立SQL表

Room的SQL表有很多部分,我會依照上一篇講的順序使用

定義資料結構

先改造TodoItem

@Entity("TodoTable")  
data class TodoItem(  
    @PrimaryKey(true)  
    var id: Int = 0,  
    var title: String = "",  
    var content: String = "",  
    @ColumnInfo("group_name")  
    var group: String = "",  
    var done: Boolean = false  
)

// for group viewing
data class GroupInfo(  
    val group: String,  
    val finished: Int,  
    val total: Int  
)
  • @Entity:讓Room知道他是SQL的資料,並給這個表一個名字
  • @PrimaryKey(true):定義primary key,並讓資料庫自動生成id的值
  • @ColumnInfo:因為group與SQL語法撞名,所以換一個在sql中的column name

定義操作介面

@Dao  
interface TodoDao {  
    @Insert(onConflict = OnConflictStrategy.IGNORE)  
    fun insert(todoItem: TodoItem)  
  
    @Delete  
    fun delete(todoItem: TodoItem)  
  
    @Update  
    fun update(todoItem: TodoItem)  
  
    @Query("""  
	    SELECT group_name AS `group`, 
	    SUM(done) AS finished,  
	    COUNT(*) AS total    
	    FROM TodoTable
	    GROUP BY group_name""")  
	fun getGroupsInfo(): Flow<List<GroupInfo>>
	
    @Query("Select * From TodoTable Where group_name = :group Order by title ASC")  
    fun getGroupItems(group: String): Flow<List<TodoItem>>  
    @Query("Select * From TodoTable Where group_name = :group Order by done ASC, title ASC") 
    fun getGroupItemsSorted(group: String): Flow<List<TodoItem>>  
}
  • @Insert(OnConflictStrategy.IGNORE):在primary key衝突的時候採取的方式
  • update, insert, delete語法都不需要特別註明,會自動獲取傳遞的參數
  • @Query可以選擇單一參數傳遞,但是查詢語句是靜態的,如果想要動態語句,可以使用RawQuery;Query也可以做delete update等工作

建立資料庫

@Database(entities = [TodoItem::class], version = 1, exportSchema = false)  
abstract class AppDatabase: RoomDatabase() {  
    abstract fun todoDao(): TodoDao  
}

這步就是將前面所有的部件匯入到RoomDatabase 中,它裡面已經寫好一些功能,只需要傳遞操作方式與entity屬性就好

這邊我示範一下converter的用法,因為Color是ULong,並不是可以儲存的datatype,所以要變換型別

@Database(entities = [Task::class], version = 1, exportSchema = false)  
@TypeConverters(Converters::class)  
abstract class AppDatabase : RoomDatabase() {  
    abstract fun taskDao(): TaskDao  
}  
  
// since color has unstorable type ULong, using ARGB to transform  
class Converters {  
    @TypeConverter  
    fun fromColor(color: Color): Int = color.toArgb() // 存成 ARGB Int  
    @TypeConverter  
    fun toColor(value: Int): Color = Color(value) // 從 ARGB Int 還原  
}

連接ViewModel

因為最後一步就是將viewmodel與database連接,直接使用viewmodel而不需要知道資料來源是MVVM架構的核心

class GlobalViewModelFactory(private val todoDao: TodoDao) : ViewModelProvider.Factory {  
    override fun <T : ViewModel> create(modelClass: Class<T>): T {  
        if (modelClass.isAssignableFrom(GlobalVM::class.java)) {  
            @Suppress("UNCHECKED_CAST")  
            return GlobalVM(todoDao) as T  
        }  
        throw IllegalArgumentException("Unknown ViewModel class")  
    }  
}
// At GlobalVM
class GlobalVM(private val todoDao: TodoDao) : ViewModel()

這邊看起來很恐怖,但其實它就是自定義ViewModel的產生方式,因為原本的Factory只能建構無參數的ViewModel,但我們現在的ViewModel有一個參數,所以要自定義生成方式

在大型專案裡,將資料來源(網路API、資料庫)獨立成Repository 幾乎是必備的,它能幫助專案保持結構清晰
但在小型專案中,如果資料來源單純,直接讓 ViewModel 呼叫 DAO 也沒問題


上一篇
Day 21:使用SQL保留資料
系列文
現代Android jetpack compose開發入門22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言